package com.linkgie.galaxyframework.web;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.linkgie.galaxyframework.data.PaginableData;
import com.linkgie.galaxyframework.utils.Pagination;
import com.linkgie.galaxyframework.utils.RuntimeStatus;

/**
 * 服务回复；
 * 
 * @author Hike
 *
 * @param <T>
 */
public class ServiceResponse<T> implements PaginableData<T> {

	private static ServiceResponse<Void> SUCCESS_RESPONSE = new ServiceResponse<Void>(RuntimeStatus.SUCCESS,
			null);

	private int code;

	private RuntimeStatus status;

	private String message;

	private String redirectTo;

	/**
	 * 数据；
	 */
	private T data;

	/**
	 * 数据类型；
	 * <p>
	 * 如果设置此属性，则序列化输出时，data 内容仅按照设置的类型输出；
	 * <p>
	 * 此属性不参与反序列化，以避免产生安全问题；
	 */
	@JsonIgnore
	private Class<?> dataType;

	/**
	 * 是否输出数据类型；
	 * <p>
	 * 注：此属性不参与序列化和反序列化；
	 */
	@JsonIgnore
	private boolean outputDataType = false;

	/**
	 * 分页；
	 */
	private Pagination pagination;

	/**
	 * @return
	 */
	public int getCode() {
		return code;
	}

	public boolean isSuccess() {
		return code == RuntimeStatus.SUCCESS.CODE || code == RuntimeStatus.REDIRECT.CODE;
	}

	public void setCode(int code) {
		this.code = code;
		this.status = RuntimeStatus.valueOf(code);
	}

	public RuntimeStatus getStatus() {
		return status;
	}

	public void setStatus(RuntimeStatus status) {
		if (status == null) {
			throw new IllegalArgumentException("Null status!");
		}
		this.status = status;
		this.code = status.CODE;
	}

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}

	@Override
	public T getData() {
		return data;
	}

	public void setData(T data) {
		this.data = data;
	}

	/**
	 * 数据类型；
	 * <p>
	 * 如果设置此属性，则序列化输出时，data 内容仅按照设置的类型输出；
	 * <p>
	 * 此属性不参与反序列化，以避免产生安全问题；
	 * 
	 * @return the dataType
	 */
	public Class<?> getDataType() {
		return dataType;
	}

	/**
	 * @param dataType the dataType to set
	 */
	public void setDataType(Class<?> dataType) {
		this.dataType = dataType;
	}

	/**
	 * 是否输出数据类型；
	 * <p>
	 * 注：此属性仅包内可见，并且不参与序列化和反序列化；
	 * 
	 * @return the outputDataType
	 */
	boolean isOutputDataType() {
		return outputDataType;
	}

	/**
	 * @param outputDataType the outputDataType to set
	 */
	void setOutputDataType(boolean outputDataType) {
		this.outputDataType = outputDataType;
	}

	/**
	 * @return the pagination
	 */
	@Override
	public Pagination getPagination() {
		return pagination;
	}

	/**
	 * @param page the pagination to set
	 */
	public void setPagination(Pagination page) {
		this.pagination = page;
	}

	public ServiceResponse() {
		this.code = RuntimeStatus.SUCCESS.CODE;
	}

	public ServiceResponse(int statusCode, String statusMessage, T data) {
		this.message = statusMessage;
		this.data = data;
		setCode(statusCode);
	}

	public ServiceResponse(RuntimeStatus status, T data) {
		this.message = status.DESCRIPTION;
		this.data = data;
		setStatus(status);
	}

	private ServiceResponse(RuntimeStatus status) {
		this.message = status.DESCRIPTION;
		setStatus(status);
	}

	public String getRedirectTo() {
		return redirectTo;
	}

	public void setRedirectTo(String redirectTo) {
		this.redirectTo = redirectTo;
	}

	public static <T> ServiceResponse<T> redirect(RuntimeStatus status, String url) {
		ServiceResponse<T> resp = new ServiceResponse<T>(status);
		resp.setRedirectTo(url);
		return resp;
	}

	public static <T> ServiceResponse<T> redirect(RuntimeStatus status, String message, String url) {
		ServiceResponse<T> resp = new ServiceResponse<T>(status);
		resp.setMessage(message);
		resp.setRedirectTo(url);
		return resp;
	}

	public static <T> ServiceResponse<T> redirect(int code, String message, String url) {
		ServiceResponse<T> resp = new ServiceResponse<T>();
		resp.setCode(code);
		resp.setMessage(message);
		resp.setRedirectTo(url);
		return resp;
	}

	public static <T> ServiceResponse<T> valueOf(T data) {
		return new ServiceResponse<T>(RuntimeStatus.SUCCESS, data);
	}

	public static ServiceResponse<Void> success() {
		return SUCCESS_RESPONSE;
	}

	public static <T> ServiceResponse<T> success(T data) {
		return success(data, null, false);
	}

	/**
	 * 创建成功回复；
	 * 
	 * @param <T>            数据类型；
	 * @param data           数据对象；
	 * @param dataType       数据类型；如果指定了类型，则仅输出该类型中包含的属性；如果未指定，则输出全部属性；
	 * @param outputDataType 是否在回复中包含数据类型的全名；
	 * @return
	 */
	public static <T> ServiceResponse<T> success(T data, Class<?> dataType) {
		return success(data, dataType, false);
	}

	/**
	 * 创建成功回复；
	 * 
	 * @param <T>            数据类型；
	 * @param data           数据对象；
	 * @param dataType       数据类型；如果指定了类型，则仅输出该类型中包含的属性；
	 * @param outputDataType 是否在回复中包含数据类型的全名；
	 * @return
	 */
	public static <T> ServiceResponse<T> success(T data, Class<?> dataType, boolean outputDataType) {
		ServiceResponse<T> response = new ServiceResponse<T>(RuntimeStatus.SUCCESS, data);
		response.setDataType(dataType);
		response.setOutputDataType(outputDataType);
		return response;
	}

	public static <T> ServiceResponse<T> badRequest(T data) {
		return new ServiceResponse<T>(RuntimeStatus.BAD_REQUEST, data);
	}

	/**
	 * 请求参数无效；
	 * 
	 * @param <T>
	 * @param data
	 * @return
	 */
	public static <T> ServiceResponse<T> invalidParameters(T data) {
		return new ServiceResponse<T>(RuntimeStatus.INVALID_PARAMETERS, data);
	}

	/**
	 * 业务状态无效；
	 * 
	 * @param <T>
	 * @param data
	 * @return
	 */
	public static <T> ServiceResponse<T> invalidBusinessState(T data) {
		return new ServiceResponse<T>(RuntimeStatus.INVALID_BUSINESS_STATE, data);
	}

	/**
	 * 服务器内部错误；
	 * 
	 * @param <T>
	 * @param data
	 * @return
	 */
	public static <T> ServiceResponse<T> serverError(T data) {
		return new ServiceResponse<T>(RuntimeStatus.SERVER_INNER_ERROR, data);
	}

	public static <T> ServiceResponse<T> create(RuntimeStatus status, T data) {
		return new ServiceResponse<T>(status, data);
	}

	public static <T> ServiceResponse<T> create(RuntimeStatus status, T data, String message) {
		return new ServiceResponse<T>(status.CODE, message, data);
	}

	/**
	 * @return the sUCCESS_RESPONSE
	 */
	public static ServiceResponse<Void> getSUCCESS_RESPONSE() {
		return SUCCESS_RESPONSE;
	}

	/**
	 * @param sUCCESS_RESPONSE the sUCCESS_RESPONSE to set
	 */
	public static void setSUCCESS_RESPONSE(ServiceResponse<Void> sUCCESS_RESPONSE) {
		SUCCESS_RESPONSE = sUCCESS_RESPONSE;
	}

}
